/*
* Copyright (c) 2008, BSC (Barcelon Supercomputing Center)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the <organization> nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY BSC ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/* Code modified and adapted to work with the dfruntime by Boris Arnoux, INRIA */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <sys/time.h>
#include <time.h>


#include <dfruntime.h>
#include <owm_management.h>

#define NB 12
#define BS 32
#define FLOATS_PER_LINE 15

/* Debug control*/
#define D_print_mat(X,Y)
/*
#define PRINT_VALUES
#define D_print_mat(X,Y)  print_mat(X,Y)
*/
#define FALSE (0)
#define TRUE (1)


#ifdef SPU_CODE
#include <spu_intrinsics.h>
#endif

typedef float (*p_block_t)[BS];

//p_block_t A[NB][NB];
//p_block_t origA[NB][NB];
//p_block_t L[NB][NB];
//p_block_t U[NB][NB];

long usecs (void)
{
    struct timeval t;

    gettimeofday(&t,NULL);
    return t.tv_sec*1000000+t.tv_usec;
}


// Allocate clean block is here modified to used dfrt :
p_block_t allocate_clean_block()
{
    int i,j;
    p_block_t p;

    p=(p_block_t) dfrt_owm_malloc(BS*BS*sizeof(float));
    if (p!=NULL){
        for (i = 0; i < BS; i++)
            for (j = 0; j < BS; j++)
                p[i][j]=0.0;
    }
    else {
        printf ("OUT OF MEMORY!!!!!!!!!!!!!!!\n");
        exit (-1);
    }
    return (p);
}

void  print_pointer_structure(char *s, p_block_t A[NB][NB])
{
    int ii, jj;
    p_block_t p;

    printf ("\nStructure for matrix %s\n", s);

    for (ii = 0; ii < NB; ii++) {
        for (jj = 0; jj < NB; jj++) {
            p =  A[ii][jj];
            if (p!=NULL) printf ("%p ",p);
            else printf ("    *    ");
        }
        printf ("\n");
    }
}
void  print_structure(char *s, p_block_t A[NB][NB])
{
    int ii, jj;
    p_block_t p;

    printf ("\nStructure for matrix %s\n", s);

    for (ii = 0; ii < NB; ii++) {
        for (jj = 0; jj < NB; jj++) {
            p =  A[ii][jj];
            if (p!=NULL) printf ("x");
            else printf (" ");
        }
        printf ("\n");
    }
}


void print_mat (char *s, p_block_t mat[NB][NB])
{
    int ii, jj, i, j, maxNB=NB;
    p_block_t p;

    if (NB<14) print_pointer_structure (s, mat);
    else print_structure (s, mat);

#ifdef PRINT_VALUES
    printf ("values of matrix %s\n", s);
    if (NB*BS>FLOATS_PER_LINE) {
        maxNB=FLOATS_PER_LINE/BS;
        printf ("very large matrix. Only printing %d blocks\n",maxNB);
    }

    for (ii = 0; ii < maxNB; ii++) {
        for (i = 0; i < BS; i++) {
            for (jj = 0; jj < maxNB; jj++) {
                p=mat[ii][jj];
                for (j = 0; j < BS; j++) {
                    if (p != NULL) printf ("%f ", p[i][j]);
                    else printf ("%f ", 0.0);
                }
            }
            printf ("\n");
        }
    }
#endif
}

p_block_t ( *genmat ()) [NB][NB]
{
    int  i, j, ii, jj;
    int null_entry;

    long int init_val;

    init_val = 1325;

    p_block_t (*ret) [NB][NB] = dfrt_owm_malloc( sizeof( p_block_t[NB][NB] ));
    /* structure */
    for (ii=0; ii<NB; ii++)
        for (jj=0; jj<NB; jj++){
            null_entry=FALSE;
            if ((ii<jj) && (ii%3 !=0)) null_entry =TRUE;
            if ((ii>jj) && (jj%3 !=0)) null_entry =TRUE;
            if (ii%2==1) null_entry=TRUE;
            if (jj%2==1) null_entry=TRUE;
            if (ii==jj) null_entry=FALSE;
            if (ii==jj-1) null_entry=FALSE;
            if (ii-1 == jj) null_entry=FALSE;
            if (null_entry==FALSE){
                (*ret)[ii][jj] = allocate_clean_block();
                if ((*ret)[ii][jj]==NULL) {
                    printf("Out of memory\n");
                    exit(1);
                }
            }
            else (*ret)[ii][jj] = NULL;
        }

    /* Initialization */ /* preguntar Cela valores razonable */
    for (ii = 0; ii < NB; ii++)
        for (jj = 0; jj < NB; jj++)
        {
            p_block_t p;
            p = (*ret) [ii][jj];
            if (p!=NULL)
                for (i = 0; i < BS; i++)
                    for (j = 0; j < BS; j++) {
                        init_val = (3125 * init_val) % 65536;
                        p[i][j] = 0.0001;
                        if (ii == jj){
                            if (i==j) p[i][j] = -20000 ;
                            if (((i-1)==j) || (i==(j-1))) p[i][j] = 10000 ;
                        }
                    }
        }
    return ret;
}


//#pragma css task input (ref_block, to_comp) output (mse)
void are_blocks_equal (float ref_block[BS][BS], float to_comp[BS][BS], float *mse)
{
    int i,j;
    float diff;

    *mse = 0.0;
    for (i = 0; i < BS; i++)
        for (j = 0; j < BS; j++) {
            diff = ref_block[i][j]-to_comp[i][j];
            *mse += diff*diff;
        }
}


// TODO : structures related to blocks are equal.

void compare_mat (p_block_t X[NB][NB], p_block_t Y[NB][NB])
{
    int ii, jj;
    float sq_error[NB][NB];
    p_block_t Zero_block;
    int some_difference = FALSE;

    Zero_block = allocate_clean_block();

    for (ii = 0; ii < NB; ii++)
        for (jj = 0; jj < NB; jj++) {
            if (X[ii][jj] == NULL)
                if (Y[ii][jj] == NULL)
                    sq_error[ii][jj] = 0.0f;
                else
                    are_blocks_equal(Zero_block, Y[ii][jj],&sq_error[ii][jj]);
            else
                are_blocks_equal(X[ii][jj], Y[ii][jj],&sq_error[ii][jj]);
        }

    /* Alternative to put wait on */
//#pragma css finish
    DEBUG ("Comparison of matrices at %p and %p",X,Y);
    for (ii = 0; ii < NB; ii++)
        for (jj = 0; jj < NB; jj++)
            //#pragma css wait on (&sq_error[ii][jj])
            if (sq_error[ii][jj] >0.0000001L) {
                DEBUG ("block [%d, %d]: detected mse = %.20lf",ii,jj,sq_error[ii][jj]);
                some_difference =TRUE;
                //exit(1);
            }
    if ( some_difference == FALSE) DEBUG ("matrices are identical");

}


//#pragma css task input(A) output(L, U)
void split_block (float A[BS][BS], float L[BS][BS], float U[BS][BS])
{
    int i, j, k;

    for (i=0; i<BS; i++)
        for (j=0; j<BS; j++) {
            if (i==j)     { L[i][j]=1.0;      U[i][j]=A[i][j]; }
            else if (i>j) { L[i][j]=A[i][j];  U[i][j]=0.0;}
            else           { L[i][j]=0.0;      U[i][j]=A[i][j]; }
        }
}


//#pragma css task input(Src) output(Dst)
void copy_block (float Src[BS][BS], float Dst[BS][BS] )
{
    int i, j;

    for (i=0; i<BS; i++)
        for (j=0; j<BS; j++)
            Dst[i][j]=Src[i][j];
}

void split_mat (p_block_t LU[NB][NB],p_block_t L[NB][NB],p_block_t U[NB][NB])
{
    int ii, jj;
    p_block_t block;

    for (ii=0; ii<NB; ii++)
        for (jj=0; jj<NB; jj++){
            if (ii==jj) {                               /* split diagonal block */
                L[ii][ii] = allocate_clean_block();
                U[ii][ii] = allocate_clean_block();
                split_block (LU[ii][ii],L[ii][ii],U[ii][ii]);
            } else {                                    /* copy non diagonal block to ... */
                if (LU[ii][jj] != NULL) {
                    block = allocate_clean_block();
                    copy_block(LU[ii][jj],block);
                } else block = NULL;
                if (ii>jj) {                             /*         ...either L ... */
                    L[ii][jj]=block;
                    U[ii][jj]=NULL;
                } else {                                /*         ... or U */
                    L[ii][jj]=NULL;
                    U[ii][jj]=block;
                }
            }
        }
}

void copy_mat (p_block_t Src[NB][NB], p_block_t Dst[NB][NB])
{
    int ii, jj;
    p_block_t block;

    for (ii=0; ii<NB; ii++)
        for (jj=0; jj<NB; jj++)
            if (Src[ii][jj] != NULL) {
                block = allocate_clean_block();
                copy_block(Src[ii][jj],block);
                Dst[ii][jj] = block;
            } else
                Dst[ii][jj]=NULL;
}

//#pragma css task output(Dst)
void clean_block (float Dst[BS][BS] )
{
    int i, j;

    for (i=0; i<BS; i++)
        for (j=0; j<BS; j++)
            Dst[i][j]=0.0;
}

void clean_mat (p_block_t Src[NB][NB])
{
    int ii, jj;

    for (ii=0; ii<NB; ii++)
        for (jj=0; jj<NB; jj++)
            if (Src[ii][jj] != NULL) {
                /* this will work in sequential, but would require waiting for all uses of Src[ii][jj] in parallel
           free (Src[ii][jj]);
           Src[ii][jj]=NULL;
*/
                clean_block(Src[ii][jj]);
            }
}
//#pragma css task input(a,b) inout(c)
void block_mpy_add(float a[BS][BS], float b[BS][BS], float c[BS][BS])
{
    int i, j, k;

    for (i=0; i<BS; i++)
        for (j=0; j<BS; j++)
            for (k=0; k<BS; k++)
                c[i][j] += a[i][k]*b[k][j];
}
/* C = A*B */
void sparse_matmult (p_block_t A[NB][NB], p_block_t B[NB][NB], p_block_t C[NB][NB])
{
    int ii, jj, kk;

    for (ii=0; ii<NB; ii++) {
        for (jj=0; jj<NB; jj++) {
            for (kk=0; kk<NB; kk++) {
                if ((A[ii][kk] != NULL) && (B[kk][jj] !=NULL )) {
                    if (C[ii][jj] == NULL) C[ii][jj] = allocate_clean_block();
                    block_mpy_add (A[ii][kk], B[kk][jj], C[ii][jj]);
                }
            }
        }
    }

}




// Should be done in parallel but...
void lu0(p_block_t block)
{

    int i, j, k;


    for (k=0; k<BS; k++) {
        for (i=k+1; i<BS; i++) {
            block[i][k] = block[i][k] / block[k][k];
            for (j=k+1; j<BS; j++)
                block[i][j] -= block[i][k] * block[k][j];
        }
    }


}


struct wrdesc nullwrdesc = {
   0,false,{0,0}
};

struct bdiv_infos {
    DFRT_INFO_CONTENT;
    struct wrdesc *wrdesc;

};

struct bdiv_frame_struct {
    struct bdiv_infos *bdiv_infos;
    int sc;
    struct frame_struct * out;
    p_block_t diag;
    p_block_t row;
} __attribute__((__packed__));





//#pragma css task input(diag) inout(row)
void bdiv()
{

    struct bdiv_frame_struct * fp =
            (struct bdiv_frame_struct*) dfrt_get_cfp();
    DEBUG( "Entering BDIV");
    int i, j, k;
    p_block_t diag = fp->diag;

    p_block_t row = fp->row;

     // TODO : how do we free that ? (maybe add a field)
    struct owm_wrdesc * owm_wrdesc_diag =
            calloc( 1, sizeof( struct owm_wrdesc ));
    struct owm_wrdesc * owm_wrdesc_row =
            calloc( 1, sizeof( struct owm_wrdesc ));

    owm_wrdesc_diag->decvec = NULL;
    owm_wrdesc_diag->ndecvec = 0;
    owm_wrdesc_diag->nwrvec = 0;
    owm_wrdesc_diag->wrvec = NULL;

    owm_wrdesc_row->decvec = NULL;
    owm_wrdesc_row->ndecvec = 0;
    owm_wrdesc_row->nwrvec = 1;
    owm_wrdesc_row->wrvec = calloc(1, sizeof( struct wrvec ));


    owm_wrdesc_row->wrvec->offset = 0;
    owm_wrdesc_row->wrvec->size = BS*BS*sizeof(float);

    diag = dfrt_owm_acquire(diag, BS*BS*sizeof(float), owm_wrdesc_diag, false);
    row = dfrt_owm_acquire(row, BS*BS*sizeof(float), owm_wrdesc_row, true);



    for (i=0; i<BS; i++) {
        for (k=0; k<BS; k++) {
            row[i][k] = row[i][k] / diag[k][k];
            for (j=k+1; j<BS; j++)
                row[i][j] -= row[i][k]*diag[k][j];
        }
    }

    dfrt_owm_release(fp->diag);
    dfrt_owm_release(fp->row);

    // Does the tdec after row update.
    dfrt_delayeduntil_tdecrease(fp->out, fp->row);

    // Ends.
    dfrt_tend();

}


struct misc_info bdiv_misc_infos = {
    "bdiv", "Block divide by trigonal block"
};

struct bdiv_infos bdiv_infos = {
 bdiv, // Fname
 1, // Sc initial
 1, // numret ( TODO : choose b/w numret =totdec or numret = #wrdesc )
 1, // Set recursive.
 sizeof( struct bdiv_frame_struct),
 &bdiv_misc_infos,
 &nullwrdesc
};



// BMOD -------------------------

struct bmod_infos {
    DFRT_INFO_CONTENT;
    struct wrdesc *wrdesc;
};

struct bmod_frame_struct {
    struct bmod_infos *bmod_infos;
    int sc;
    struct frame_struct * out;
    p_block_t row;
    p_block_t col;
    p_block_t inner;
} __attribute__((__packed__));


//#pragma css task input(row,col) inout(inner)
void bmod()
{
    struct bmod_frame_struct * fp =
            (struct bmod_frame_struct*) dfrt_get_cfp();
    DEBUG( "Entering BMOD");
    p_block_t row,col,inner;
    row = fp->row;
    col = fp->col;
    inner = fp->inner;

    int i, j, k;

    struct owm_wrdesc * owm_wrdesc_inner = calloc( 1, sizeof( struct owm_wrdesc ));
    struct owm_wrdesc * owm_wrdesc_row = calloc( 1, sizeof( struct owm_wrdesc ));
    struct owm_wrdesc * owm_wrdesc_col = calloc( 1, sizeof( struct owm_wrdesc ));

    owm_wrdesc_row->decvec = NULL;
    owm_wrdesc_row->ndecvec = 0;
    owm_wrdesc_row->nwrvec = 0;
    owm_wrdesc_row->wrvec = NULL;

    owm_wrdesc_col->decvec = NULL;
    owm_wrdesc_col->ndecvec = 0;
    owm_wrdesc_col->nwrvec = 0;
    owm_wrdesc_col->wrvec = NULL;

    owm_wrdesc_inner->decvec = NULL;
    owm_wrdesc_inner->ndecvec = 0;
    owm_wrdesc_inner->nwrvec = 1;
    owm_wrdesc_inner->wrvec = calloc(1, sizeof( struct wrvec ));


    owm_wrdesc_inner->wrvec->offset = 0;
    owm_wrdesc_inner->wrvec->size = BS*BS*sizeof(float);

    row = dfrt_owm_acquire(row, BS*BS*sizeof(float), owm_wrdesc_row, false);
    col = dfrt_owm_acquire(col, BS*BS*sizeof(float), owm_wrdesc_col, false);
    inner = dfrt_owm_acquire(inner, BS*BS*sizeof(float), owm_wrdesc_inner, true);

    for (i=0; i<BS; i++) {
        for (j=0; j<BS; j++) {
            for (k=0; k<BS; k++) {
                inner[i][j] -= row[i][k]*col[k][j];
             }
         }
     }

    // We use the originals for releasing : others might just be working
    // copies.
    dfrt_owm_release( fp->row );
    dfrt_owm_release( fp->col );
    dfrt_owm_release( fp->inner );

    dfrt_delayeduntil_tdecrease(fp->out, fp->inner);
    dfrt_tend();

}


struct misc_info bmod_misc_infos = {
    "bmod", "Block substract by kronecker product"
};

struct bmod_infos bmod_infos = {
 bmod, // Fname
 1, // Sc initial
 1, // numret ( TODO : choose b/w numret =totdec or numret = #wrdesc )
 1, // Set recursive.
 sizeof( struct bmod_frame_struct),
 &bmod_misc_infos,
 &nullwrdesc
};


// FWD --------------------

struct fwd_infos {
    DFRT_INFO_CONTENT;
    struct wrdesc *wrdesc;

};


struct fwd_frame_struct {
    struct fwd_infos * fwd_infos;
    int sc;
    struct frame_struct * out;
    // Args :
    p_block_t diag;
    p_block_t col;

}__attribute__((__packed__));

//#pragma css task input(diag) inout(col)
void fwd()
{
    struct fwd_frame_struct * fp =
            (struct fwd_frame_struct * ) dfrt_get_cfp();
    DEBUG( "Entered FWD");
    p_block_t diag = fp->diag;
    p_block_t col = fp->col;

    struct owm_wrdesc * owm_wrdesc_diag = calloc( 1, sizeof( struct owm_wrdesc ));
    struct owm_wrdesc * owm_wrdesc_col = calloc( 1, sizeof( struct owm_wrdesc ));

    owm_wrdesc_diag->decvec = NULL;
    owm_wrdesc_diag->ndecvec = 0;
    owm_wrdesc_diag->nwrvec = 0;
    owm_wrdesc_diag->wrvec = NULL;

    owm_wrdesc_col->decvec = NULL;
    owm_wrdesc_col->ndecvec = 0;
    owm_wrdesc_col->nwrvec = 1;
    owm_wrdesc_col->wrvec = calloc(1, sizeof( struct wrvec ));


    owm_wrdesc_col->wrvec->offset = 0;
    owm_wrdesc_col->wrvec->size = BS*BS*sizeof(float);

    // These are now working blocks : we will use the fp-> originals
    // when release needs to be done.
    diag =  dfrt_owm_acquire(diag, BS*BS*sizeof(float), owm_wrdesc_diag, true);
    col = dfrt_owm_acquire(col, BS*BS*sizeof(float), owm_wrdesc_col, true);

    int i, j, k;

    for (j=0; j<BS; j++) {
        for (k=0; k<BS; k++) {
            for (i=k+1; i<BS; i++) {
                col[i][j] -= diag[i][k]*col[k][j];
            }
        }
    }

    dfrt_delayeduntil_tdecrease(fp->out, fp->col);

    dfrt_owm_release(fp->diag);
    dfrt_owm_release(fp->col);


    dfrt_tend();

}


struct misc_info fwd_misc_infos = {
    "fwd", "fwd (see code, col substract inverse )"
};

struct fwd_infos fwd_infos = {
 fwd, // Fname
 1, // Sc initial
 1, // numret ( TODO : choose b/w numret =totdec or numret = #wrdesc )
 1, // Set recursive.
 sizeof( struct fwd_frame_struct),
 &fwd_misc_infos,
 &nullwrdesc
};



// Waiter -----------------
struct waiter_infos {
    DFRT_INFO_CONTENT;

};

struct waiter_frame_struct {
    struct waiter_infos *waiter_infos;
    int sc;
    // Nothing here.
    p_block_t (*A)[NB][NB];
    p_block_t (*origA)[NB][NB];


};

void waiter () {
    struct waiter_frame_struct *fp  =
            (struct waiter_frame_struct*) dfrt_get_cfp();

    DEBUG( "Entered WAITER");
    p_block_t (*A)[NB][NB] = fp->A;
    p_block_t (*origA)[NB][NB] = fp->origA;

    long t_end=usecs();
    DEBUG( "Finished processing.");
    D_print_mat("A=LxU", *A);

    p_block_t (*L) [NB][NB] = dfrt_owm_malloc( sizeof( p_block_t[NB][NB]));
    p_block_t (*U) [NB][NB] = dfrt_owm_malloc( sizeof( p_block_t[NB][NB]));

    split_mat (*A, *L, *U);
    D_print_mat("L", *L);
    D_print_mat("U", *U);

    clean_mat (*A);
    D_print_mat("Zero A", *A);

    sparse_matmult (*L, *U, *A);
    D_print_mat("LxU", *A);
    compare_mat (*origA, *A);
    dfrt_tend();

    //#pragma css finish

    //time = ((double)(t_end-t_start))/1000000;
    //printf("time to compute = %f\n", time);
}

struct misc_info waiter_misc_infos = {
    "LU_entry waiter", "... LU entry waiter"
};

struct waiter_infos waiter_infos = {
 waiter, // Fname
 1, // Sc initial TODO : is dynamically defined here...
 0, // numret ( TODO : choose b/w numret =totdec or numret = #wrdesc )
 0, // Setnon recursive.
 sizeof( struct waiter_frame_struct),
 &waiter_misc_infos
};

// LU_ENTRY ---------------

struct LU_entry_infos {
    DFRT_INFO_CONTENT;
};


void LU_entry_for_k();

struct LU_entry_frame_struct {
    struct LU_entry_infos *LU_entry_infos;
    int sc;
    // input arguments;
    p_block_t (*A)[NB][NB];
    p_block_t (*origA)[NB][NB];
    int k;
}__attribute__((__packed__));

struct misc_info LU_entry_misc_infos = {
    "LU_entry", "... LU entry ( see code )"
};

struct LU_entry_infos LU_entry_infos = {
 LU_entry_for_k, // Fname
 1, // Sc initial TODO : is dynamically defined here...
 0, // numret ( TODO : choose b/w numret =totdec or numret = #wrdesc )
 1, // Set recursive.
 sizeof( struct LU_entry_frame_struct),
 &LU_entry_misc_infos
};


void LU_entry_for_k(){

    struct LU_entry_frame_struct * fp =
            (struct LU_entry_frame_struct *) dfrt_get_cfp();
    DEBUG( "LU_entry_for_k : %d", fp->k);

    p_block_t (*A)[NB][NB] = fp->A;
    int kk = fp->k;

    struct frame_struct *next;
    if ( kk < NB - 1 ) {
        // Create a LU_entry for next k and decrease it in the end.
        struct LU_entry_frame_struct * nLU =
                (struct LU_entry_frame_struct *) dfrt_tcreate( 1, sizeof( struct frame_struct));
        nLU->LU_entry_infos = &LU_entry_infos;
        nLU->A = A;
        nLU->origA = fp->origA;
        nLU->k = kk+1;
        next = ( struct frame_struct * ) nLU;

    } else if ( kk == NB-1) {
        // create a waiter :
        struct waiter_frame_struct * wfs =
                (struct waiter_frame_struct *) dfrt_tcreate(1, sizeof( struct frame_struct));
        wfs->waiter_infos = &waiter_infos;
        wfs->A = A;
        wfs->origA = fp->origA;
        next = (struct frame_struct * ) wfs;
    } else {
        // Abort;
        abort();
    }
    int ii, jj;

    int count_to_wait = 1;

    //for (kk=0; kk<NB; kk++) {
    struct owm_wrdesc * owm_wrdesc_diag =
            calloc( 1, sizeof( struct owm_wrdesc ));
    owm_wrdesc_diag->decvec = NULL;
    owm_wrdesc_diag->ndecvec = 0;
    owm_wrdesc_diag->nwrvec = 1;
    owm_wrdesc_diag->wrvec = calloc(1, sizeof( struct wrvec ));


    owm_wrdesc_diag->wrvec->offset = 0;
    owm_wrdesc_diag->wrvec->size = BS*BS*sizeof(float);

    // AKK is a working copy. We will use the full expression as original
    // pointer for releasing.
    //p_block_t Akk = dfrt_owm_acquire((*A)[kk][kk], BS*BS*sizeof(float),owm_wrdesc_diag, true);
    lu0((*A)[kk][kk] );

    //dfrt_owm_release((*A)[kk][kk]);

    for (jj=kk+1; jj<NB; jj++) {

        if ((*A)[kk][jj] != NULL) {
            //fwd(A[kk][kk], A[kk][jj]);
            ++ count_to_wait;
            struct fwd_frame_struct * fwd_frame_struct
                    = (struct fwd_frame_struct * ) dfrt_tcreate(1,sizeof(struct fwd_frame_struct) );
            fwd_frame_struct->fwd_infos = &fwd_infos;
            fwd_frame_struct->col = (*A)[kk][jj];
            fwd_frame_struct->diag = (*A)[kk][kk];
            fwd_frame_struct->out = next;
            dfrt_delayed_tdecrease((struct frame_struct *) fwd_frame_struct);


        }
    }

    for (ii=kk+1; ii<NB; ii++) {
        if ((*A)[ii][kk] != NULL) {
            //bdiv (A[kk][kk], A[ii][kk]);
            ++ count_to_wait;
            struct bdiv_frame_struct * bdiv_frame_struct
                    = (struct bdiv_frame_struct*) dfrt_tcreate(1,sizeof(struct bdiv_frame_struct));
            bdiv_frame_struct->bdiv_infos = &bdiv_infos;
            bdiv_frame_struct->diag = (*A)[kk][kk];
            bdiv_frame_struct->row = (*A)[ii][kk];
            bdiv_frame_struct->out = next;
            dfrt_delayed_tdecrease((struct frame_struct *)bdiv_frame_struct);
        }
    }

    for (ii=kk+1; ii<NB; ii++) {
        if ((*A)[ii][kk] != NULL) {
            for (jj=kk+1; jj<NB; jj++) {
                if ((*A)[kk][jj] != NULL) {
                    if ((*A)[ii][jj]==NULL) (*A)[ii][jj]=allocate_clean_block();
                    //bmod(A[ii][kk], A[kk][jj], A[ii][jj]);
                    struct bmod_frame_struct * bmod_frame_struct =
                            (struct bmod_frame_struct *) dfrt_tcreate(1, sizeof(struct bmod_frame_struct));

                    bmod_frame_struct->bmod_infos = &bmod_infos;
                    bmod_frame_struct->col = (*A)[kk][jj];
                    bmod_frame_struct->row = (*A)[ii][kk];
                    bmod_frame_struct->inner = (*A)[ii][jj];
                    bmod_frame_struct->out = next;

                    dfrt_delayed_tdecrease((struct frame_struct *)bmod_frame_struct);
                    ++ count_to_wait;

                }
            }
        }

    }

    // Updates the "next" with the rith value for SC.
    next->sc = count_to_wait;

    // Tdecs for hismelf as well.
    dfrt_delayed_tdecrease(next);



    // End :
    dfrt_tend();


}




void main_task()
{
    long t_start,t_end;
    double time;

    //#pragma css start

    p_block_t (*A)[NB][NB] = genmat();
    D_print_mat("A", *A);

    p_block_t (*origA) [NB][NB] = dfrt_owm_malloc( sizeof( p_block_t[NB][NB]));

    copy_mat (*A, *origA);
    D_print_mat("reference A", *origA);

    t_start=usecs();
    //LU(A);
    struct LU_entry_frame_struct *lu_entry =
            (struct LU_entry_frame_struct * ) dfrt_tcreate( 1, sizeof( struct LU_entry_frame_struct));
    lu_entry->LU_entry_infos = &LU_entry_infos;
    lu_entry->A = A;
    lu_entry->origA = origA;
    lu_entry->k = 0;

    dfrt_delayed_tdecrease((struct frame_struct *)lu_entry);
    dfrt_tend();

}




int main() {

    dfrt_init(2,2,main_task);
}



// TODO : proper main, info struct
